package epoch.game.core.context;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import epoch.game.core.action.IAction;
import epoch.game.core.action.ICallAction;
import epoch.game.core.action.ICallbackSafe;
import epoch.game.core.actor.ASystem;
import epoch.game.core.actor.ATimer;
import epoch.game.core.actor.Actor;
import epoch.game.core.config.Config;
import epoch.game.core.distr.Node;
import epoch.game.core.distr.call.Call;
import epoch.game.core.distr.call.CallContext;
import epoch.game.core.distr.call.CallReturn;
import epoch.game.core.support.Param;
import epoch.game.core.thread.ServerThread;

public class Context {
	private static Logger log = LogManager.getLogger(Context.class);

	private static ASystem AS;
	
	public static void init(int nThread) {
		AS = new ASystem(nThread);
		if (Config.ACTOR_MONITOR) {
			AS.startMonitor(Config.ACTOR_NOHEART_TIME);
		}
	}
	public static void fini() {
		if (AS != null) {
			AS.stop();
		}
	}
	public static void setNode(Node node) {
		AS.setNode(node);
	}
	
	public static long now() {
		return AS.now();
	}
	public static void status() {
		AS.status();
	}
	
	/**
	 * 创建一个默认的Actor
	 * 
	 * @param name
	 * @return
	 */
	public static Actor createActor(String name) {
		Actor actor = new Actor();
		return AS.create(actor, name);
	}
	
	public static Actor createActor(Actor actor, String name) {
		return AS.create(actor, name);
	}
	
	public static boolean destroyActor(Actor actor, boolean force) {
		return AS.destroy(actor, force);
	}
	
	public static boolean destroyActor(Actor actor) {
		return AS.destroy(actor);
	}
	
	public static boolean regTimer(ATimer timer) {
		return AS.regTimer(timer);
	}
	public static boolean send(Actor actor, IAction action) {
		if (AS.isAt(actor)) {
			action.action();
			return true;
		} else {
			return actor.send(action);
		}
	}
	public static <T> void doAction(Actor actor, final ICallAction<T> callAction, final ICallbackSafe<T> callback) {
		ServerThread thread = null;
		int selfId = AS.selfId();
		if (selfId == 0) {
			Thread t = Thread.currentThread();
			if (t instanceof ServerThread) {
				thread = (ServerThread)t;
			}
		}
		
		if (actor == null || AS.isAt(actor)) {
			boolean ok = true;
			T callbackResult = null;
			try {
				callbackResult = callAction.action();
			} catch (Throwable e) {
				log.error("doAction", e);
				ok = false;
			}
			if (callback != null) {
				callback.callback(ok, callbackResult);
			}
		} else {
			if (callback != null) {
				final ServerThread sthread = thread;
				actor.send(() -> {
					boolean ok = true;
					T callbackResult = null;
					try {
						callbackResult = callAction.action();
					} catch (Throwable e) {
						log.error("doAction", e);
						ok = false;
					}
					final boolean ok2 = ok;
					final T callbackResult2 = callbackResult;
					if (selfId > 0) {
						AS.send(selfId, () -> {
							callback.callback(ok2, callbackResult2);
						});
					} else {
						sthread.addAction(() -> {
							callback.callback(ok2, callbackResult2);
						});
					}
				});
			} else {
				actor.send(() -> {
					callAction.action();
				});
			}
		}
	}

    public static void listen(Object callback, CallContext context, boolean handleTimeOut) {
    	AS.listen(callback, context, handleTimeOut);
    }
    public static boolean fillCall(Call call) {
    	Actor actor = AS.actor();
    	if (actor != null) {
	    	call.isActor = true;
			call.fromPortId = actor.actorId();
			call.callbackId = actor.getAndResetCallbackId();
			return true;
    	}
    	return false;
    }
    public static void callRet(CallReturn callReturn, Object result) {
    	callReturn.setReturns(result);
    	AS.onReturn(callReturn);
    }
    public static void callRet(CallReturn callReturn) {
    	AS.onReturn(callReturn);
    }
    public static void returns(Object returns){
		Actor actor = AS.actor();
		if (actor != null) {
			actor.returns = returns;
		}
	}
    public static void returns(Object... returns){	
		Actor actor = AS.actor();
		if (actor != null) {
			Param param = new Param(returns);
			actor.returns = param;
		}
	}
    public static Actor currentActor() {
    	return AS.actor();
    }
    public static Call currentCall() {
    	Actor actor = AS.actor();
    	if (actor != null) {
    		return actor.currentCall();
    	} else
    		return null;
    }
    public static int fromNodeId() {
    	Call call = currentCall();
    	return call != null ? call.fromNodeId : 0;
    }
    
    public static void tryReturn(Call call, Object returns) {
    	if(call.callbackId>0){
    		CallReturn callReturn = call.createCallReturn();
    		Context.forceReturn(callReturn, returns);
		}
    }
    
    public static void forceReturn(CallReturn callReturn, Object returns) {
    	Actor actor = AS.actor();
    	if (actor != null) {
    		actor.callReturn = callReturn;
    		actor.returns = returns;
    		actor.doReturn();
    	}
    }
    public static CallReturn detachCallReturn() {
    	CallReturn ret = null;
    	Actor actor = AS.actor();
    	if (actor != null) {
    		ret = actor.callReturn;
    		actor.callReturn = null;
    	}
    	return ret;
    }
    
    public static <T> T waitFor(IAction action) {
    	return waitFor(action, 60000);
    }
    public static <T> T waitFor(IAction action, long timeout) {
    	Actor actor = AS.actor();
    	if (actor == null) {
    		return null;
    	}
    	actor.listenSync();
    	action.action();
		return actor.waitFor(timeout);
    }
    public static <T> T callBlock(Actor actor, ICallAction<T> action) {
    	return Context.waitFor(()->{
			Call call = new Call();
			Context.fillCall(call);
			actor.send(()->{
				T r = action.action();
				CallReturn callReturn = call.createCallReturn();
				callReturn.setReturns(r);
				Context.callRet(callReturn);
			});
		});
    }
}